#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#include "reader.h"

/*
 * Interactive card explotation program.
 *
 * @author Jonathan Walsh
 */

/* The handle of the card reader*/
static int i_reader = -1;

/* The mode we are operating in*/
typedef enum readmode_t { UNCONFIGURED, DEFAULT, RAW } readmode;
static readmode reader_mode = UNCONFIGURED;

/*
 * Card storage space for later usage
 */
#define MAX_NUM_CARDS 256   /*256 should be enough!*/
static char **cards[MAX_NUM_CARDS];
static int cards_sizes[MAX_NUM_CARDS];
static int cards_index = 0;

/*
 * Perform a clean exit from the program
 */
int clean_exit() {
  int i;
  for ( i = 0; i < cards_index; i++ ) {
    free(cards[i]);
  }

  if ( i_reader != -1 ) close_device_port( i_reader );

  printf( "\nGoodbye! Come back soon. :D\n" );
  exit(0);
  return 0;
}

/*
 * Read a single menu choice
 */
int get_menu_choice() {
    printf( "> " ); 
    char buf[16];
    fgets( buf, sizeof(buf), stdin );
    return atoi(buf);
}

int menu_main() {
  int go = 1, selection = -1;
  while ( go ) {
    go = 0;
    
    printf( "\n  Main Menu\n" );
    printf( "   1) Default Three Track / Credit Card Mode\n" );
    printf( "   2) Raw Card Mode\n" );
    printf( "   9) EXIT PROGRAM (I'll miss you)\n" );
    selection = get_menu_choice();

    /* If someone has selected a mode, and we are already configured, need 
     *  to unconfigure myself first */
    if ( selection >= 1 && selection <= 3 && reader_mode != UNCONFIGURED ) {
        printf( "TODO: Disconnect from card reader\n" );
    }
    
    switch( selection ) {
    case 1:
      printf( "TODO: INIT READER FOR NORMAL\n" );
      i_reader = get_device_port();
      reader_mode = DEFAULT;
      break;
    case 2:
      printf( "TODO: INIT READER FOR RAW\n" );
      i_reader = -1;
      reader_mode = RAW;
      break;
    case 9:
      clean_exit();
      break;
    default:
      printf( "Invalid Entry!\n" );
      go = 1;
    }
    
  }
  
  return selection;
}

int menu_write_card_to_file( char *card, int size ) {
  char fn[150];
  memset( fn, '\0', 150 );
  printf( "Enter Filename? " );
  char *ret = fgets( fn, sizeof(fn), stdin );
  if ( ret == NULL ) {
    printf( "Error reading user input! Bailing out\n" );
    exit(1);
  }
  fn[strlen(fn)-1] = '\0'; /*strip the newline*/
  
  printf( "Writing file..." );
  fflush(stdout);
  
  int fd = open( fn, O_WRONLY | O_CREAT,
		 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
  if ( fd == -1 ) {
    printf( "\nERROR: Failed to open output file, card not written!\n" );
    perror( "open" );     
    return 1;
  }
  
  int reti = write( fd, card, size );
  if ( reti != size ) {
    printf( "\nERROR: Some write problem... Card probably not written\n" );
    if ( reti == -1 ) perror( "write" );
    reti = 1;
  } else reti = 0;
  
  close(fd);
  
  printf( " Done!\n" );
  
  return reti;
}


int menu_raw_card() {
  int card_read = 0, go = 1;
  char card[256];
  int size, selection;
  
  while ( go ) {
    printf( "\n  Raw Card Menu\n" );
    printf( "    1) Read New Card\n" );
    printf( "    2) Write Last Card to File (binary)\n" );
    printf( "    3) Print Last Card Raw (hex)\n" );
    printf( "    9) Go back to Main Menu\n" );
    selection = get_menu_choice();
    
    if ( selection >= 2 && selection <= 3 && !card_read ) {
      printf( "ERROR: I haven't read a card yet!\n" );
      continue;
    }
    
    switch( selection ) {
    case 1:
      printf( "TODO: READ NEW CARD\n" );
      card_read = 1;
      strcpy( card, "LOLLERSKATES" );
      size = strlen( "LOLLERSKATES" );
      break;
    case 2:
      menu_write_card_to_file( card, size );
      break;
    case 3:
      printf( "TODO PRINT RAW CARD\n" );
      break;
    case 9:
      go = 0;
      break;
    default:
      printf( "Invalid Entry!\n" );
    }
    
  }
  return selection;
}

int menu_cc() {
  int card_read = 0, go = 1;
  char *card = (char*)calloc(READ_BUFFER_SIZE,sizeof(char));
  int size = 0, selection;
  char scratch[READ_BUFFER_SIZE];
  int active = -1;
  credit_card_data d;

  while ( go ) {
    printf( "\n  Default / Credit Card Menu" );
    if ( card_read ) printf( " (CARD ACTIVE" );
    if ( active != -1 ) printf( " id=%i", active );
    printf( ")\n" );    
    
    printf( "    1) Read New Card\n" );
    printf( "    2) Write Active Card to File (binary)\n" );
    printf( "    3) Print Active Card Raw (hex)\n" );
    printf( "    4) Print Active Card with Delims\n" );
    printf( "    5) Print Active Card Credit Card Data\n" );
    printf( "    6) Store Active Card in Memory\n" );
    printf( "    7) Reload Stored Card to Active\n" );
    printf( "    8) Print Stored List\n" );
    printf( "    9) Go back to Main Menu\n" );
    selection = get_menu_choice();
    
    if ( selection >= 2 && selection <= 6 && !card_read ) {
      printf( "ERROR: I haven't read a card yet!\n" );
      continue;
    }
    
    switch( selection ) {
    case 1:
      printf( "Please swipe the card now..." ); fflush(stdout);
      size = read_card( i_reader, &card, READ_BUFFER_SIZE );
      printf( " New card read!\n" );
      printf( "Processing..." ); fflush(stdout);
      //TODO: put any extra checks here
      card_read = 1, active = -1;
      printf( " Done!\n" );
      
      break;
    case 2:
      menu_write_card_to_file( card, size );
      break;
    case 3:
      print_card_by_raw_bytes( card, size );
      break;
    case 4:
      print_card_by_values( card, size, 1 );
      break;
    case 5:
      parse_card_as_credit_card( card, size, &d );
      print_credit_card( &d );
      break; 
    case 6:
      cards[cards_index] = calloc( size, sizeof(char) );
      memcpy( cards[cards_index], card, size );
      cards_sizes[cards_index] = size;
      active = cards_index;
      printf( "Last Card Stored as: %i\n", cards_index );
      cards_index++;
      break;
    case 7:
      if (cards_index==0) { 
	printf( "ERROR: No cards are stored\n" );
	break;
      }
      
      /*Get the stored card id*/
      memset( scratch, '\0', sizeof(scratch) );
      printf( "Enter Card ID (0-%i)? ", cards_index-1 );
      if ( fgets( scratch, sizeof(scratch), stdin ) == NULL ) {
	printf( "Error reading user input! Bailing out\n" );
	exit(1);
      }
      
      int sid = atoi(scratch);
      if ( sid < 0 || sid >= cards_index ) {
	printf( "ERROR: Card id %i not stored\n", sid );
	break;
      }
      active = sid;
      card_read = 1;
      
      size = cards_sizes[sid];
      memcpy( card, cards[sid], size );
      break;
    case 8:
      if ( cards_index == 0 ) printf( "No cards stored\n" );
      else printf( "Cards from 0 to %i are stored\n", cards_index-1 );
      break;
    case 9:
      go = 0; 
      break;
    default:
      printf( "Invalid Entry!\n" );
    }
    
  }

  free(card);

  return selection;
}

int main( int argc, char *argv[] ) {
  int selection;
  
  printf( "**** Card Reader Exploitation Potential Exploration and Survey Interface ****\n" );
  
  while ( 1 ) {
    
    selection = menu_main();
    
    printf( "Selection = %i\n", selection);
    
    if ( selection == 1 ) menu_cc();
    else if ( selection == 2 )  menu_raw_card();
    
  }
  
  return 0;
}
